package com.slz3.business.frame.response;

import com.slz3.business.frame.steward.Steward;
import com.slz3.business.frame.util.LocalDT;
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.Map;

import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8;
import static org.springframework.web.reactive.function.BodyInserters.fromPublisher;
import static org.springframework.web.reactive.function.server.ServerResponse.badRequest;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;

public class RR {

    // #####                        #####
    // #####     attribute key      #####
    // #####                        #####

    public static final String RES_INFO = ResInfo.ResInfoBuilder.class.getName();
    // “接力” 用于 module-menu-steward 中一条业务线在不同静态方法中的存取 ResSafeValid-Executor
    public static final String RELAY_SAFE_VALID = RR.class.getName().concat("_RELAY_SAFE_VALID");

    // #####                        #####
    // #####   response info code   #####
    // #####                        #####

    public static final String SUCCESS = "success";
    public static final String SAFE_VALID = "safeValid";
    public static final String UN_SAFE_VALID = "unSafeValid";
    public static final String EXECUTOR = "executor";
    public static final String EXCEPTION = "exception";

    // #####                        #####
    // #####   response info error  #####
    // #####                        #####

    public static final String ILLEGAL_PARAM = "illegal parameter";

    // #####                        #####
    // #####        methods         #####
    // #####                        #####

    public static <T> T attribute(ServerRequest req, String attributeKey) {
        return (T) req.attribute(attributeKey).get();
    }

    public static ResInfo resInfo(ServerRequest req) {
        ResInfo resInfo = attribute(req, RR.RES_INFO);
        return resInfo;
    }

    public static Map<String, Object> resDetail(ServerRequest req) {
        return RR.resInfo(req).getDetail();
    }

    public static boolean hasResErr(ServerRequest req) {
        return resDetail(req).keySet().stream().filter(key -> !key.equals(RR.class.getSimpleName())).count() > 0;
    }

    // #####

    public static Mono<ServerResponse> success(ServerRequest req, ResInfo source) {
        source.setCode("success");
        source.setStatus(HttpStatus.OK.value());
        return resMono(req, source, ok());
    }

    public static Mono<ServerResponse> fail(ServerRequest req, ResInfo source) {
        source.setCode("fail");
        source.setStatus(HttpStatus.BAD_REQUEST.value());
        return resMono(req, source, badRequest());
    }

    public static Mono<ServerResponse> error(ServerRequest req, ResInfo source) {
        source.setCode("fail");
        source.setStatus(HttpStatus.BAD_REQUEST.value());
        return resMono(req, source, badRequest());
    }

    private static Mono<ServerResponse> resMono(ServerRequest req, ResInfo source, ServerResponse.BodyBuilder bodyBuilder) {
        ResInfo target = resInfo(req);
        Steward.merge(source, target);
        ResInfoRRDetail detail = ((ResInfoRRDetail) target.getDetail().get(RR.class.getSimpleName()));
        detail.setResTimestamp(System.currentTimeMillis());
        detail.setDuration(detail.getResTimestamp() - detail.getReqTimestamp());
        target.setTimestamp(LocalDT.getDateTimeOfTimestamp(detail.getResTimestamp()).toString());
        return bodyBuilder.contentType(APPLICATION_JSON_UTF8)
                .body(fromPublisher(Mono.just(target), ResInfo.class));
    }

    public static ResInfo initReqResInfo(Object obj) {

        if (obj == null) return null;

        if (obj instanceof ServerRequest) {

            ServerRequest req = (ServerRequest) obj;

            return initReqResInfoByServerRequest(req);

        } else if (obj instanceof ServerWebExchange) {

            ServerWebExchange exchange = (ServerWebExchange) obj;

            return initReqResInfoByServerWebExchange(exchange);

        } else {

            return null;

        }

    }

    private static ResInfo initReqResInfoByServerWebExchange(ServerWebExchange exchange) {
        ResInfo resInfo = ResInfo.builder()
                .code("fail")
                .theme("exception")
                .status(HttpStatus.BAD_REQUEST.value())
                .detail(new HashMap<>())
                .build();
        resInfo.getDetail().put(RR.class.getSimpleName(),
                ResInfoRRDetail.builder()
                        .reqTimestamp(System.currentTimeMillis())
                        .reqType(exchange.getRequest().getMethodValue())
                        .reqPath(exchange.getRequest().getPath().pathWithinApplication().value())
                        .reqParams(exchange.getRequest().getQueryParams().toString())
                        .reqIp(exchange.getRequest().getHeaders().getHost().toString())
                        .build()
        );
        return resInfo;
    }

    private static ResInfo initReqResInfoByServerRequest(ServerRequest req) {
        ResInfo resInfo = ResInfo.builder().detail(new HashMap<>()).build();
        resInfo.getDetail().put(RR.class.getSimpleName(),
                ResInfoRRDetail.builder()
                        .reqTimestamp(System.currentTimeMillis())
                        .reqType(req.methodName())
                        .reqPath(req.path())
                        .reqParams(req.queryParams().toString())
                        .reqIp(req.headers().host().toString())
                        .build()
        );
        return resInfo;
    }

}
